AOK Scenario (SCN/SCX) format, by DiGiT
Tab-size: 4

The AOK scenario format is actually shared by Age of Empires I and Age of Empires II. Every file has a 4-byte version string at the very beginning which identifies the program that made the scenario. (See "Versions" table below.) After the reasonably short header, the scenario data is compressed using the DEFLATE method. I have not found any value to store the uncompressed length, so the data must be decompressed into some kind of expandable buffer.

Versions:
1.10	Age of Empires I
1.18	Age of Empires II
1.21	Age of Empires II - TC expansion
- I've put comments next to data members that are version-specific.

The scenario data actually has room for 16 players, not the 9 that are needed by AOE and AOK (with GAIA). Beware when enabling more than 8 players, it has not been tested. Also, as far as I've seen, all the data in the scenario is treated as signed by AOK, which can lead to tricks such as negative tributes, but also opens up the possibility for easy crashing from overflows and the like. Any editor made to manipulate this format must take care with the data. AOK is very sensitive about this data being exactly how it likes it, so even seemingly harmless changes like zero-ing a useless data section can crash it.

I've written this file in pseudo-C so someone who is marginally familiar with C or a similar language will be able to quickly understand. For those of you that don't know any C, I'd suggest taking a look at cplusplus.com's tutorial on C/C++ variables, datatype, and constants (http://www.cplusplus.com/doc/tutorial/tut1-2.html). Below are a couple typedefs that I use as shorthand throughout the file.

typedef long BOOL;	//most boolean values are stored as 4 bytes

struct POINT
{
	long x, y;
};

struct String
{
	short len;
	char data[len];	//this is not possible in C, of course. Thus pseudo-C. :-)
};


General layout:
Header -> Compressed Data
Compressed Data:
CHeader -> Messages/Cinematics -> Player Data2 -> Global Victory -> Map -> Units -> Player Data3 -> Triggers -> AI Files


**************** Scenario Starts Here ****************

struct Header
{
	char version[4];
	long length;	//length of header (excluding version and self)
	const long constant = 2;
	time_t lastsave;
	long instructions_len;
	char instructions[instructions_len];
	const long padding = 0;
	long playerCount;	//thanks iberico
} header;

**** Compression Starts Here ****

struct CHeader
{
	long next_uid;
	float ver2;
	char player_names[0x10][0x100];
#if (VERSION >= 1.18)
	long string_table_names[0x10];
#endif
	struct PlayerData1
	{
		BOOL active;
		BOOL human;
		long civilization; //(see IDs at bottom)
		const long constant = 0x00000004;
	} player_data[0x10];
	long u_long;	//usually 1
	char u_char;	//always 0?
	float unknown3;	//always -1?
	String orginial_filename;
} cheader;

struct M_and_C
{
#if ((VERSION == 1.18) || (VERSION == 1.21 && ver2 == 1.21))
	const long strings[5];
#elif (VERSION == 1.21 && ver2 == 1.22)
	const long strings[6];
#endif
	String instructions, hints, victory, defeat, history;
#if (VERSION == 1.21 && ver2 == 1.22)
	String scouts;
#endif
	String pg_cinem, vict_cinem, loss_cinem, background;
	BOOL bitmap_included;
	long bitmap_x, bitmap_y;
	short whocares;				//seems to be -1 if there is a bitmap, 1 otherwise
	BITMAP_INFO_HEADER bmp_info;
	char bitmap[];	//size must be calculated from bmp_info
} msgs_and_cinem;

struct PlayerData2
{
	String unk[0x20];
	String ai[0x10];
	struct AIFile
	{
		const long nulls[2] = 0;
		long length;
		char file[length];
	} ai_files[0x10];
	char aitype[0x10];	//thanks iberico
	const long separator = 0xFFFFFF9D;
	struct Resources
	{
		long gold, wood, food, stone, orex;
		const long nulls = 0;
	} resources[0x10];
} player_data_2;

struct Victory
{
	const long seperator = 0xFFFFFF9D;
	BOOL conquest;
	const long zero1;
	long relics;
	const long zero2;
	long explored;	//0 if unused
	const long zero3;
	BOOL all;	//all custom must be met?
	long mode, score, time;	//time in 10ths of a year
} victory;

struct Diplomacy
{
	long diplomacy[0x10][0x10];
	//this seems to be the diplomacy that AOK actually reads
	//(00 = allied, 01 = neutral, 02 = ?, 03 = enemy)
	char nulls[0x2D00] = 0;
	const long separator = 0xFFFFFF9D;	//why here?!

	long AlliedVictory[0x10];
	//also in PlayerData3, thanks iberico. AOK doesn't read this one.
}

struct Techs
{
	long num_dtechs[0x10];
	long disabled_techs[0x10][0x1E];
	long num_dunits[0x10];
	long disabled_units[0x10][0x1E];
	long num_dbuildings[0x10];
	long disabled_bldgs[0x10][0x14];
	const long nulls2[2] = 0;
	long all_techs;
	long starting_age[0x10];	//-1 = None selected, 0 = Dark, 1 = Feudal, 2 = Castle, 3 = Imperial, 4 = Post-Imperial
} disables;

struct Map
{
	const long separator = 0xFFFFFF9D;
	long player1_pos[2];	//camera position for player 1 (see below)
	long aitype;	//only in TC?
	long mapx, mapy;
	Terrain[x][y][3];	//ID, elevation, NULL;
} map;

*** Units ***
Size	Value
-------------------
4	09 00 00 00 (maybe number of players)
struct PlayerData4
{
	float resources[6];
	float pop_limit;
} player_data_4[8];
Var*10	Unit Data (see below). Organized by player (GAIA first), with a 4-byte count preceding each section.

struct Unit
{
	float x-pos, y-pos;
	const float whoknows = 2;
	long id;
	short constant;
	const char whocares = 2;
	float rotation;	//in radians
	short frame;	//initial animation frame
	long garrison;
};

*** Player Data3 ***
Size	Value
-------------------
4	09 00 00 00
64*8	Player Data3 (see below)
8	Double: 1.6

struct PlayerData3
{
	long length;
	char constant_name[length]; //"Player #"
	float camera[2]; //for player 1, this is the camera position in the editor.
	short unknown[2];	//something to do with camera? (close values)
	bool allied_victory;
	short count_diplomacy;
	char diplomacy[count_diplomacy]; //00 = allied, 01 = neutral, 02 = ?, 03 = enemy; GAIA is always enemy.
	long diplomacy2[count_diplomacy]; //00 = GAIA, 01 = self, 02 = allied, 03 = neutral, 04 = enemy
	long color;
	float n;
	long nulls[n];
	char nulls2[9];
	static long end = -1; //in 1.21 only
};

*** Triggers ***
Size	Value
-------------------
1	00
4	Number of triggers = N
V*N	Triggers (see below)
4*N	List Order

struct POINT
{
	long x, y;
};

struct Trigger
{
	BOOL enabled;
	BOOL looping;
	char unknown;
	bool objective;	//listed in objectives?
	long desc_order;	//order in objectives
	long zeros;
	long desc_len;
	char description[desc_len];
	long name_len;		//max 44
	char name[name_len];

	long num_effects;
	Effect effects[num_effects];	//size varies with strings
	long effect_order[num_effects];

	long num_conditions;
	Condition conditions[num_conditions];
	long condition_order[num_conditions];
}

struct Effect
{
	long type;	//see constants below
	long check;	//always = 0x17. For internal use by AOK (and AOKTS).
	long ai_goal;	//AI Script goal
	long amount;	//used for resources, hp, attack
	long resource;	//resource type (stone = 2)
	long diplomacy;	//state for change diplomacy (see constants below)
	long num_selected;	//number of units selected
	long location_unit;	//Unit ID for location setting
	long unit_type;	//unit constant for create object, etc.
	long player_source;
	long player_target;
	long technology;	//see "Complete Constant Lists" in AOKH utilities
	long stringid;	//as in string table
	long unknown;
	long display_time;	//for Display Instructions
	long trigger;	//trigger index for Activate/Deactivate Trigger
	POINT location;
	POINT area_ll;	//lower-left corner of area
	POINT area_ur;	//upper-right corner
	long unit_group;
	long unit_type;	//Civilian, Military, Building, Other
	long instruction_panel;
	long text_len;
	char text[text_len];	//Instructions/Chat
	long sound_len;
	char sound_file[sound_len];
	long unit_ids[num_selected];
};

struct Condition
{
	long type;	//see constants below
	long check;	//always = 0x10 (for internal use)
	long amount;	//of objects, difficult level
	long resource;	//resource type (see constants below)
	long unit_object;
	long unit_location;
	long unit_type;
	long player;
	long technology;	//see "Complete Constant Lists" in AOKH utilities
	long timer;
	long unknown;
	POINT area_ll;
	POINT area_ur;
	long unit_group;	//see constants below
	long unit_type;	//Civilian, Military, Building, Other
	long ai_signal;
};

*** AI Files ***
Length	Value
-------------------------
4	Unknown (usually 0)
4	Number of AI files included

*** Various Constants ***

enum Player_Colors
{
	Blue,
	Red,
	Green,
	Yellow,
	Cyan,
	Purple,
	Gray,
	Orange
};

enum Civilizations
{
	Britons = 1,
	Franks,
	Goths,
	Teutons,
	Japanese,
	Chinese,
	Byzantines,
	Persians,
	Saracens,
	Turks,
	Vikings,
	Mongols,
	Celts,
	Spanish,
	Aztecs,
	Mayans,
	Huns,
	Koreans
}

Const	Value
-------------------------
AI Map Types:
9	Arabia
0A	Archipelago
0B	Baltic
0C	Black forest
0D	Coastal
0E	Continental
0F	Crater lake
10	Fortress
11	Gold rush
12	Highland
13	Islands
14	Mediterranean
15	Migration
16	Rivers
17	Team islands
18	
19	Scandinavia
1A	
1B	Yucatan
1C	Salt marsh
1D	
1E	King of the hill
1F	Oasis
21	Nomad

Unit Groups:
0	Archer
2	Trade Boat
3	Building
4	Civilian
6	Soldier
12	Cavalry
13	Siege Weapon
18	Priest
20	Transport Boat
21	Fishing Boat
22	War Boat
23	Conquistador
27	Walls
30	Flags
36	Cavalry Archer
39	Gates
44	Hand Cannoneer
45	2-handed Swordsman
46	Pikeman
47	Scout Cavalry
49	Farm
50	Spearman
51	Packed Siege Weapon
52	Tower
54	Unpacked Siege Weapon
58	Sheep
59	King

Resource Type:
0B	Population

Effects:
1	Change Diplomacy
2	Research Technology
3	Send Chat
4	Play Sound
5	Send Tribute
6	Unlock Gate
7	Lock Gate
8	Activate Trigger
9	Deactivate Trigger
A	AI Script Goal
B	Create Object
C	Task Object
D	Declare Victory
E	Kill Object
F	Remove Object
10	Change View
11	Unload
12	Change Ownership
13	Patrol
14	Display Instructions
15	Clear Instructions
16	Freeze Unit
17	Use Advanced Buttons
18	Damage Object
19	Place Foundation
1A	Change Object Name
1B	Change Object HP
1C	Change Object Attack
1D	Stop Unit
1E  Snap View
1F  Unknown
20  Enable Tech
21  Disable Tech
22  Enable Unit
23  Disable Unit
24  Flash Objects

Conditions:
1	Bring Object to Area 
2	Bring Object to Object 
3	Own Objects 
4	Own Fewer Objects 
5	Objects in Area 
6	Destroy Object 
7	Capture Object 
8	Accumulate Attribute 
9	Research Tehcnology 
A	Timer 
B	Object Selected 
C	AI Signal 
D	Player Defeated 
E	Object Has Target 
F	Object Visible 
10	Object Not Visible 
11	Researching Technology 
12	Units Garrisoned
13	Difficulty Level
14  Own Fewer Foundations
15  Selected Objects in Area
16  Powered Objects in Area
17  Units Queued Past Pop Cap

enum ResourceTypes
{
	RES_Food,
	RES_Wood,
	RES_Stone,
	RES_Gold,
	RES_Relics = 7,
	RES_Population = 11,
	RES_Kills = 20,
	RES_Technologies,
	RES_Villager Population = 37,
	RES_Military Population = 40,
	RES_Conversions,
	RES_Razings = 43,
	RES_Kill Ratio
};

enum DifficultyLevels
{
	Easiest,
	Standard,
	Moderate,
	Hard,
	Hardest
};